module vmm

#include <sys/mman.h>

fn C.mmap() voidptr
fn C.munmap() int

pub fn mallocmp(sz int) voidptr {
    pagesize := 4096
    oldsz := sz
    mut mpsz := sz
    mpsz += if (mpsz%pagesize)==0 { 0 } else { mpsz%pagesize }
    ptr := C.mmap(C.NULL, mpsz, C.PROT_READ | C.PROT_WRITE | C.PROT_EXEC, C.MAP_ANON | C.MAP_PRIVATE, -1, 0)
    if ptr == voidptr(0) {
        C.abort()
    }
    println("mp $oldsz, $mpsz, $ptr")
    return ptr
}
pub fn freemp(ptr voidptr, sz int) int {
    pagesize := 4096
    mut mpsz := sz
    mpsz += if (mpsz%pagesize)==0 { 0 } else { mpsz%pagesize }
    rv := C.munmap(ptr, mpsz)
    return rv
}


// what is Exclusion ranges overlap
// should be call GC_disable somewhere

pub fn mallocgc(sz int) voidptr {
    ptr := C.GC_malloc(sz)
    return ptr
}
pub fn mallocuc(sz int) voidptr {
    ptr := C.GC_malloc_uncollectable(sz)
    return ptr
}
pub fn callocgc(n int, sz int) voidptr {
    ptr := C.GC_malloc(n*sz)
    return ptr
}
pub fn reallocgc(ptr voidptr, sz int) voidptr {
    res := C.GC_realloc(ptr, sz)
    return res
}
pub fn freegc(ptr voidptr) {
    C.GC_free(ptr)
}


///////////////
pub fn set_finalizer(obj voidptr, fnptr voidptr, cd voidptr) {
    mut oldfn := voidptr(0)
    mut oldcd := voidptr(0)

    C.GC_register_finalizer(obj, fnptr, cd, &oldfn, &oldcd)
    if obj == voidptr(0) {
        C.printf("obj %p, fnptr %p, cd %p, oldfn %p, oldcd %p\n",
                 obj, fnptr, cd, oldfn, oldcd)
    }
    if oldfn != voidptr(0) || oldcd != voidptr(0) {
        C.printf("Duplicate call set_finalizer? (%p, %p, %p) (%p, %p)\n",
                 obj, fnptr, cd, oldfn, oldcd)
    }
}
pub fn unset_finalizer(obj voidptr) {
    mut oldfn := voidptr(0)
    mut oldcd := voidptr(0)

    C.GC_register_finalizer(obj, voidptr(0), voidptr(0), &oldfn, &oldcd)
}

pub fn isheapptr(obj voidptr) bool {
    return C.GC_is_heap_ptr(obj) != 0
}

fn C.malloc_usable_size() size_t

pub fn usable_size(ptr voidptr) int {
    if isheapptr(ptr) {
        return C.GC_size(ptr)
    }else{
        return int(C.malloc_usable_size(ptr))
    }
}

pub fn collect1() int {
    return C.GC_collect_a_little()
}
pub fn gcollect() {
    C.GC_gcollect()
}

pub fn thread_isreg() bool {
    return C.GC_thread_is_registered() != 0
}
pub fn register_my_thread() {
    // C.GC_register_my_thread()
}
pub fn pthread_create() {
    
}

/////////////
// header is GC_StackBase, plus extra
pub struct StackInfo {
    pub mut:
    membase voidptr
    regbase voidptr

    // extra
    handle voidptr
    stksz int
    stktop voidptr // membase + stksz
}

pub fn get_my_stackbottom() StackInfo {
    mut this := StackInfo {}
    // alloc_lock() // hang!!!
    this.handle = C.GC_get_my_stackbottom(&this)
    // alloc_unlock()
    return this
}
pub fn set_stackbottom(si StackInfo) {
    C.GC_set_stackbottom(si.handle, &si)
}

pub fn alloc_lock() { C.GC_alloc_lock() }
pub fn alloc_unlock() { C.GC_alloc_unlock() }

